package com.htbb.common.msgBodyAdvice;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import com.alibaba.fastjson.JSON;
import com.htbb.common.base.MappingConfig;
import com.htbb.common.base.exception.BizException;
import com.htbb.common.util.security.AESUtil;
import com.htbb.common.util.security.SignUtil;
import com.htbb.model.personalInsure.dto.Channel;

/**
 * 请求报文解密与签名校验
 * @author liulongli
 * @date 2017-5-22
 */
@ControllerAdvice
public class DecryptAndCheckSignRequestBodyAdvice implements RequestBodyAdvice {
	private final static Logger logger = LoggerFactory.getLogger(DecryptAndCheckSignRequestBodyAdvice.class);
	public static final String partnerCodeParamName = "partnerCode";

	@Override
	public Object afterBodyRead(Object arg0, HttpInputMessage arg1, MethodParameter arg2, Type arg3,
			Class<? extends HttpMessageConverter<?>> arg4) {
		return arg0;
	}

	@Override
	public HttpInputMessage beforeBodyRead(HttpInputMessage arg0, MethodParameter arg1, Type arg2,
			Class<? extends HttpMessageConverter<?>> arg3) throws IOException {
		try {
			/*根据注解判断是否需要解密与校验sign*/
			boolean decode = false;
	        if (arg1.getMethod().isAnnotationPresent(CryptionType.class)) {
	        	CryptionType cryptionType = arg1.getMethodAnnotation(CryptionType.class);
	            decode = cryptionType.decode();
	        }
			if(decode)
				return new DecryptAndCheckSignHttpInputMessage(arg0);
			else 
				return arg0;
        } catch (Exception e) {
            if(e instanceof BizException)
            	throw (BizException)e;
            
        	throw new BizException("报文解密或签名验证异常！", e);
        }
	}

	@Override
	public Object handleEmptyBody(Object arg0, HttpInputMessage arg1, MethodParameter arg2, Type arg3,
			Class<? extends HttpMessageConverter<?>> arg4) {
		return arg0;
	}

	@Override
	public boolean supports(MethodParameter arg0, Type arg1, Class<? extends HttpMessageConverter<?>> arg2) {
		return true;
	}
	
	class DecryptAndCheckSignHttpInputMessage implements HttpInputMessage {
        private HttpHeaders headers;

        private InputStream body;

        public DecryptAndCheckSignHttpInputMessage(HttpInputMessage inputMessage) throws IOException, Exception  {
            this.headers = inputMessage.getHeaders();
            /*从header中取出partnerCode */
            String partnerCode = this.headers.getFirst(partnerCodeParamName);
            if(StringUtils.isEmpty(partnerCode))
            	throw new BizException("请求报文header中缺失参数：" +partnerCodeParamName);
            
            /*根据partnerCode获取渠道信息*/
            Channel channel = MappingConfig.getChannel(partnerCode.toUpperCase());
    		if (channel == null) {
    			throw new BizException("该合作渠道暂未开通，渠道代码："+partnerCode.toUpperCase());
    		}
    		
            /**	解密body*/
    		String srcBody = IOUtils.toString(inputMessage.getBody(), "UTF-8");
    		String channelInfo = MessageFormat.format( "【渠道id：{0}，名称：{1}】", partnerCode, channel.getName());
    		logger.info(channelInfo + "接收到请求报文体(未解密)：" + srcBody);
            String postJsonStr = AESUtil.aesDecrypt(srcBody, channel.getPassword(), "UTF-8");
            logger.info(channelInfo + "接收到json报文(已解密)：" + postJsonStr);
            
            /**	校验签名sign*/
            Map<String, Object> paramMap = JSON.parseObject(postJsonStr);
            if(!SignUtil.checkSign(paramMap, channel.getPassword(), "UTF-8"))
            	throw new BizException("签名sign校验失败！");
            
            /*将partnerCode参数加入body中*/
            paramMap.put(partnerCodeParamName, partnerCode);
            postJsonStr = JSON.toJSONString(paramMap);
            this.body = IOUtils.toInputStream(postJsonStr, "UTF-8");
        }

        @Override
        public InputStream getBody() throws IOException {
            return body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }
    }

}
